#define _SCL_SECURE_NO_WARNINGS

#include <fstream>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include "XmlUtils.h"

using namespace boost;
using namespace rapidxml;
using namespace std;

string XmlUtils::SEPARATOR = ".";

///////////////////////////////////////////////////////////////////////////////
// Constructors and Destructors
///////////////////////////////////////////////////////////////////////////////

XmlUtils::~XmlUtils(void)
{
}

///////////////////////////////////////////////////////////////////////////////
// Other public methods
///////////////////////////////////////////////////////////////////////////////

const list< XmlNode* > XmlUtils::getNodes(const string& path, XmlDocument& doc)
{
    list< XmlNode* > nodes;

    // Split input path
    list<string> nodeList = splitPath(path);

    // Find first node
    XmlNode *node = findNode(nodeList, doc);

    while (node != NULL)
    {
        nodes.push_back(node);
        node = node->next_sibling((nodeList.back()).c_str());
    }

    return nodes;
}

const int XmlUtils::getINT32(const string& path, XmlDocument& doc)
{
    // Find node
    XmlNode *node = findNode(path, doc);

    // Cast & return
    return static_cast<int>(atoi(node->value()));
}

const unsigned int XmlUtils::getUINT32(const string& path, XmlDocument& doc)
{
    // Find node
    XmlNode *node = findNode(path, doc);

    // Cast & return
    return static_cast<unsigned int>(atoi(node->value()));
}

const float XmlUtils::getFLOAT32(const string& path, XmlDocument& doc)
{
    // Find node
    XmlNode *node = findNode(path, doc);

    // Cast & return
    return static_cast<float>(atof(node->value()));
}

const string XmlUtils::getString(const string& path, XmlDocument& doc)
{
    // Find node
    XmlNode *node = findNode(path, doc);

    // Cast & return
    return string(node->value());
}

const int XmlUtils::getINT32(const string& path, XmlNode* rootNode)
{
    // Split input path
    list<string> nodeList = splitPath(path);

    // Iterate over all nodes
    XmlNode *node = getNode(nodeList, rootNode);

    // Cast & return
    return static_cast<int>(atoi(node->value()));
}

const unsigned int XmlUtils::getUINT32(const string& path, XmlNode* rootNode)
{
    // Split input path
    list<string> nodeList = splitPath(path);

    // Iterate over all nodes
    XmlNode *node = getNode(nodeList, rootNode);

    // Cast & return
    return static_cast<unsigned int>(atoi(node->value()));
}

const float XmlUtils::getFLOAT32(const string& path, XmlNode* rootNode)
{
    // Split input path
    list<string> nodeList = splitPath(path);

    // Iterate over all nodes
    XmlNode *node = getNode(nodeList, rootNode);

    // Cast & return
    return static_cast<float>(atof(node->value()));
}

const string XmlUtils::getString(const string& path, XmlNode* rootNode)
{
    // Split input path
    list<string> nodeList = splitPath(path);

    // Iterate over all nodes
    XmlNode *node = getNode(nodeList, rootNode);

    // Cast & return
    return string(node->value());
}

const string XmlUtils::readAll(const string& filename) throw(...)
{
    // Read all contents from the file
    ifstream fin(filename.c_str(), ifstream::in);
    if (!fin || !fin.good() || fin.eof())
    {
        // throw exception
        string err("error reading input file");
        throw runtime_error(err);
    }

    string content;
    while (!fin.eof())
    {
        string line;
        getline(fin, line);
        content += line + '\n';
    }

    return content;
}

///////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////

const list<string> XmlUtils::splitPath(const string& path) throw(...)
{
    // Split input path using SEPARATOR
    list<string> nodeList;
    split(nodeList, path, is_any_of(SEPARATOR));
    if (nodeList.empty())
    {
        string msg = "Parsing value failed : invalid path";
        throw runtime_error(msg);
    }
    
    return nodeList;
}

XmlNode* XmlUtils::findNode(list<string> &nodeList, XmlDocument& doc) throw(...)
{
    // Iterate over all nodes
    XmlNode* rootNode = doc.first_node((nodeList.front()).c_str());
    nodeList.pop_front();
    XmlNode *node = rootNode;
    BOOST_FOREACH(string nodeName, nodeList)
    {
        node = node->first_node(nodeName.c_str());
        if (node == NULL)
        {
            string msg = "Parsing value failed : invalid node name " + nodeName;
            throw runtime_error(msg);
        }
    }

    return node;
}

XmlNode* XmlUtils::findNode(const string& path, XmlDocument& doc) throw(...)
{
    // Split input path
    list<string> nodeList = XmlUtils::splitPath(path);

    // Iterate over all all nodes
    XmlNode* rootNode = doc.first_node("level_parameters");
    if (rootNode == NULL)
    {
        string msg = "Parsing value failed : invalid node name " 
            + nodeList.front();
        throw runtime_error(msg);
    }
    nodeList.pop_front();

    return getNode(nodeList, rootNode);
}

XmlNode* XmlUtils::getNode(const list<string> &nodeList, XmlNode* rootNode) throw(...)
{
    XmlNode *node = rootNode;
    BOOST_FOREACH(string nodeName, nodeList)
    {
        node = node->first_node(nodeName.c_str());
        if (node == NULL)
        {
            string msg = "Parsing value failed : invalid node name " + nodeName;
            throw runtime_error(msg);
        }
    }

    return node;
}
